/*=============================================================================
PostProcessUtils.cpp : Post processing common utilities
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* Created by Tiago Sousa

=============================================================================*/

#include "StdAfx.h"
#include "PostProcessUtils.h"

RECT  SPostEffectsUtils::m_pScreenRect;
ITimer *SPostEffectsUtils::m_pTimer;
int SPostEffectsUtils::m_iFrameCounter=0;
SD3DSurface *SPostEffectsUtils::m_pCurDepthSurface;
CShader *SPostEffectsUtils::m_pCurrShader;
int SPostEffectsUtils::m_nColorMatrixFrameID;
float SPostEffectsUtils::m_fWaterLevel;

Vec3 SPostEffectsUtils::m_vRT = Vec3(0, 0, 0);
Vec3 SPostEffectsUtils::m_vLT = Vec3(0, 0, 0);
Vec3 SPostEffectsUtils::m_vLB = Vec3(0, 0, 0);
Vec3 SPostEffectsUtils::m_vRB = Vec3(0, 0, 0);
int SPostEffectsUtils::m_nFrustrumFrameID = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool SPostEffectsUtils::Create()
{
	assert( gRenDev );

	const int iWidth = gRenDev->GetWidth();
	const int iHeight = gRenDev->GetHeight();

	// Update viewport info
	m_pScreenRect.left=0;  
	m_pScreenRect.top=0; 

	m_pScreenRect.right = iWidth;
	m_pScreenRect.bottom = iHeight;

	if( CRenderer::CV_r_PostAA )
	{
		CreateRenderTarget("$PrevBackBuffer0", CTexture::s_ptexPrevBackBuffer[0], m_pScreenRect.right, m_pScreenRect.bottom, 1, 0, eTF_A8R8G8B8, TO_PREVBACKBUFFERMAP0);
		CreateRenderTarget("$PrevBackBuffer1", CTexture::s_ptexPrevBackBuffer[1], m_pScreenRect.right, m_pScreenRect.bottom, 1, 0, eTF_A8R8G8B8, TO_PREVBACKBUFFERMAP1);
	}
	else
	{
		SAFE_RELEASE(CTexture::s_ptexPrevBackBuffer[0]);
		SAFE_RELEASE(CTexture::s_ptexPrevBackBuffer[1]);
	}

	CreateRenderTarget("$Cached3DHud", CTexture::s_ptexCached3DHud, m_pScreenRect.right, m_pScreenRect.bottom, 1, 0, eTF_A8R8G8B8, -1);
	CreateRenderTarget("$Cached3DHudDownsampled", CTexture::s_ptexCached3DHudScaled, m_pScreenRect.right>>2, m_pScreenRect.bottom>>2, 1, 0, eTF_A8R8G8B8, -1);

	// Scaled versions of the scene target
	uint32 nWidth = m_pScreenRect.right>>1;
	uint32 nHeight = m_pScreenRect.bottom>>1;

	CreateRenderTarget("$Glow_RT1", CTexture::s_ptexGlow[0], nWidth, nHeight, 1, 0);  	
	CreateRenderTarget("$BackBufferScaled_d2", CTexture::s_ptexBackBufferScaled[0], nWidth, nHeight, 1, 0);  
	
	// Ghosting requires data overframes, need to handle for each GPU in MGPU mode
	CreateRenderTarget("$PrevFrameScaled", CTexture::s_ptexPrevFrameScaled, nWidth, nHeight, 1, 0);  		

	CreateRenderTarget("$HalfResCompositionMask", CTexture::s_ptexHalfResCompositionMask, nWidth, nHeight, 1, 0); 
	CreateRenderTarget("$BackBufferScaledTemp_d2", CTexture::s_ptexBackBufferScaledTemp[0], nWidth, nHeight, 1, 0);  

	CreateRenderTarget("$Glow_RT2", CTexture::s_ptexGlow[1], m_pScreenRect.right>>2, m_pScreenRect.bottom>>2, 1, 0);  
	CreateRenderTarget("$BackBufferScaled_d4", CTexture::s_ptexBackBufferScaled[1], m_pScreenRect.right>>2, m_pScreenRect.bottom>>2, 1, 0, eTF_A8R8G8B8, TO_BACKBUFFERSCALED_D4);     
	CreateRenderTarget("$BackBufferScaledTemp_d4", CTexture::s_ptexBackBufferScaledTemp[1], m_pScreenRect.right>>2, m_pScreenRect.bottom>>2, 1, 0);  

	CreateRenderTarget("$BackBufferScaled_d8", CTexture::s_ptexBackBufferScaled[2], m_pScreenRect.right>>3, m_pScreenRect.bottom>>3, 1, 0);        
	CreateRenderTarget("$BackBufferScaledTemp_d8", CTexture::s_ptexBackBufferScaledTemp[2], m_pScreenRect.right>>3, m_pScreenRect.bottom>>3, 1, 0);  

	CreateRenderTarget("$BackBufferScaled_d16", CTexture::s_ptexBackBufferScaled[3], m_pScreenRect.right>>4, m_pScreenRect.bottom>>4, 1, 0);        
	CreateRenderTarget("$BackBufferScaledTemp_d16", CTexture::s_ptexBackBufferScaledTemp[3], m_pScreenRect.right>>4, m_pScreenRect.bottom>>4, 1, 0);  

	CreateRenderTarget("$RainDropsAccumRT_0", CTexture::s_ptexRainDropsRT[0], m_pScreenRect.right>>2, m_pScreenRect.bottom>>2, 1, false, eTF_A8R8G8B8);           
	CreateRenderTarget("$RainDropsAccumRT_1", CTexture::s_ptexRainDropsRT[1], m_pScreenRect.right>>2, m_pScreenRect.bottom>>2, 1, false, eTF_A8R8G8B8);           
	CTexture::s_ptexRainDropsRT[0]->SetRenderTargetTile(1);
	CTexture::s_ptexRainDropsRT[1]->SetRenderTargetTile(1);

	// Water phys simulation requires data overframes, need to handle for each GPU in MGPU mode
	CreateRenderTarget("$WaterRipplesDDN_0", CTexture::s_ptexWaterRipplesDDN, 256, 256, 1, true, eTF_A8R8G8B8, TO_WATERRIPPLESMAP);           
	CreateRenderTarget("$WaterVolumeDDN", CTexture::s_ptexWaterVolumeDDN, 64, 64, 1, true, eTF_A8R8G8B8, TO_WATERVOLUMEMAP);        







#if defined(VOLUMETRIC_FOG_SHADOWS)
	CreateRenderTarget("$VolFogShadowBuf0", CTexture::s_ptexVolFogShadowBuf[0], iWidth/2, iHeight/2, 1, 0, eTF_A8R8G8B8, TO_VOLFOGSHADOW_BUF);
	CreateRenderTarget("$VolFogShadowBuf1", CTexture::s_ptexVolFogShadowBuf[1], iWidth/2, iHeight/2, 1, 0, eTF_A8R8G8B8);
#endif

	return 1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::Release()
{
	SAFE_RELEASE(CTexture::s_ptexPrevBackBuffer[0]);
	SAFE_RELEASE(CTexture::s_ptexPrevBackBuffer[1]);
	SAFE_RELEASE(CTexture::s_ptexGlow[0]);
	SAFE_RELEASE(CTexture::s_ptexGlow[1]);

  SAFE_RELEASE(CTexture::s_ptexBackBufferScaled[0]);
  SAFE_RELEASE(CTexture::s_ptexBackBufferScaled[1]);
  SAFE_RELEASE(CTexture::s_ptexBackBufferScaled[2]);
  SAFE_RELEASE(CTexture::s_ptexBackBufferScaled[3]);

	SAFE_RELEASE(CTexture::s_ptexBackBufferScaledTemp[0]);
	SAFE_RELEASE(CTexture::s_ptexBackBufferScaledTemp[1]);
	SAFE_RELEASE(CTexture::s_ptexBackBufferScaledTemp[2]);
	SAFE_RELEASE(CTexture::s_ptexBackBufferScaledTemp[3]);

  SAFE_RELEASE(CTexture::s_ptexWaterVolumeDDN);
  
  SAFE_RELEASE(CTexture::s_ptexHalfResCompositionMask);
 
	SAFE_RELEASE(CTexture::s_ptexCached3DHud);
	SAFE_RELEASE(CTexture::s_ptexCached3DHudScaled);

	SAFE_RELEASE(CTexture::s_ptexPrevFrameScaled);
	SAFE_RELEASE(CTexture::s_ptexWaterRipplesDDN);	

	SAFE_RELEASE(CTexture::s_ptexRainDropsRT[0]);
	SAFE_RELEASE(CTexture::s_ptexRainDropsRT[1]);

#if defined(VOLUMETRIC_FOG_SHADOWS)
	SAFE_RELEASE(CTexture::s_ptexVolFogShadowBuf[0]);
	SAFE_RELEASE(CTexture::s_ptexVolFogShadowBuf[1]);
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::DrawFullScreenTriangle(int nTexWidth, int nTexHeight, float z)
{    
	// No offsets required in d3d10
	float fOffsetU = 0.0f;
	float fOffsetV = 0.0f;

#if defined (DIRECT3D9)
	fOffsetU = 0.5f/(float)nTexWidth;
	fOffsetV = 0.5f/(float)nTexHeight;  
#endif

	static SVF_P3F_C4B_T2F pScreenTri[] =
	{
		{ Vec3(0, 0, 0), {{~0}}, Vec2(0, 0) },   
		{ Vec3(0, 0, 0), {{~0}}, Vec2(0, 2) },    
		{ Vec3(0, 0, 0), {{~0}}, Vec2(2, 0) },    
	};

	pScreenTri[0].xyz = Vec3( -fOffsetU,  -fOffsetV, z);
	pScreenTri[1].xyz = Vec3( -fOffsetU, 2-fOffsetV, z);
	pScreenTri[2].xyz = Vec3(2-fOffsetU,  -fOffsetV, z);

	CVertexBuffer strip(pScreenTri, eVF_P3F_C4B_T2F);
	gRenDev->DrawPrimitives(&strip, 3);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::DrawFullScreenQuad(int nTexWidth, int nTexHeight, float z, bool bAllowRectList, const RECT * pSrcRegion)
{    
	// No offsets required in d3d10
	float fOffsetU = 0.0f;
	float fOffsetV = 0.0f;

#if defined (DIRECT3D9)
	fOffsetU = 0.5f/(float)nTexWidth;
	fOffsetV = 0.5f/(float)nTexHeight;
#endif

	SVF_P3F_C4B_T2F pScreenQuad[] =
	{
		{ Vec3(-fOffsetU, -fOffsetV, z),   {{~0}}, Vec2(0, 0) },   
		{ Vec3(-fOffsetU, 1-fOffsetV, z),  {{~0}}, Vec2(0, 1) },    
		{ Vec3(1-fOffsetU, -fOffsetV, z),  {{~0}}, Vec2(1, 0) },   
		{ Vec3(1-fOffsetU, 1-fOffsetV, z), {{~0}}, Vec2(1, 1) },   
	};

	if (pSrcRegion)
	{
		const Vec4 vTexCoordsRegion(float(pSrcRegion->left) / nTexWidth,
			float(pSrcRegion->right) / nTexWidth,
			float(pSrcRegion->top) / nTexHeight,
			float(pSrcRegion->bottom) / nTexHeight);
		pScreenQuad[0].st = Vec2(vTexCoordsRegion.x, vTexCoordsRegion.z);
		pScreenQuad[1].st = Vec2(vTexCoordsRegion.x, vTexCoordsRegion.w);
		pScreenQuad[2].st = Vec2(vTexCoordsRegion.y, vTexCoordsRegion.z);
		pScreenQuad[3].st = Vec2(vTexCoordsRegion.y, vTexCoordsRegion.w);
	}

	CVertexBuffer strip(pScreenQuad, eVF_P3F_C4B_T2F);






		gRenDev->DrawPrimitivesInternal(&strip, 4, eptTriangleStrip);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::DrawScreenQuad(int nTexWidth, int nTexHeight, float x0, float y0, float x1, float y1)
{
  Vec3 vv[4];
  vv[0] = Vec3(x0, y0, 0);
  vv[1] = Vec3(x0, y1, 0);
  vv[2] = Vec3(x1, y0, 0);
  vv[3] = Vec3(x1, y1, 0);
  SVF_P3F_C4B_T2F pScreenQuad[] =  
  {
		{ Vec3(0, 0, 0), {{0}}, Vec2(0, 0) },   
    { Vec3(0, 0, 0), {{0}}, Vec2(0, 1) },    
    { Vec3(0, 0, 0), {{0}}, Vec2(1, 0) },   
    { Vec3(0, 0, 0), {{0}}, Vec2(1, 1) },   
  };

  // No offsets required in d3d10
  float fOffsetU = 0.0f;
  float fOffsetV = 0.0f;

#if defined (DIRECT3D9) || defined (XENON)

  fOffsetU = 0.5f/(float)nTexWidth;
  fOffsetV = 0.5f/(float)nTexHeight;  

#endif

  vv[0] += Vec3(-fOffsetU, -fOffsetV, 0);
  vv[1] += Vec3(-fOffsetU, -fOffsetV, 0);
  vv[2] += Vec3(-fOffsetU, -fOffsetV, 0);
  vv[3] += Vec3(-fOffsetU, -fOffsetV, 0);

  pScreenQuad[0].xyz = vv[0];
  pScreenQuad[1].xyz = vv[1];
  pScreenQuad[2].xyz = vv[2];
  pScreenQuad[3].xyz = vv[3];

  gRenDev->m_RP.m_PersFlags2 &= ~(RBPF2_COMMIT_PF);  

  CVertexBuffer strip(pScreenQuad, eVF_P3F_C4B_T2F); 




	gRenDev->DrawPrimitivesInternal(&strip, 4, eptTriangleStrip);

}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::DrawFullScreenBorder(int nTexWidth, int nTexHeight, const Vec4& vSpaces, bool bTrapeze) //left up right down
{
	const Vec4& s = vSpaces;
	Vec2	avQuads[4][4];
	if(bTrapeze)
	{
		avQuads[0][0] = Vec2(0, 0);			avQuads[0][1] = Vec2(0, 1);			avQuads[0][2] = Vec2(s.x, 1-s.w);	avQuads[0][3] = Vec2(s.x, s.y);		//left
		avQuads[1][0] = Vec2(0, 0);			avQuads[1][1] = Vec2(s.x, s.y);		avQuads[1][2] = Vec2(1-s.z, s.y);	avQuads[1][3] = Vec2(1, 0);			//up
		avQuads[2][0] = Vec2(1-s.z, s.y);	avQuads[2][1] = Vec2(1-s.z, 1-s.w);	avQuads[2][2] = Vec2(1, 1);			avQuads[2][3] = Vec2(1, 0);			//right
		avQuads[3][0] = Vec2(s.x, 1-s.w);	avQuads[3][1] = Vec2(0, 1);			avQuads[3][2] = Vec2(1, 1);			avQuads[3][3] = Vec2(1-s.z, 1-s.w);	//down
	}
	else
	{
		avQuads[0][0] = Vec2(0, 0);			avQuads[0][1] = Vec2(0, 1);		avQuads[0][2] = Vec2(s.x, 1);		avQuads[0][3] = Vec2(s.x, 0);		//left
		avQuads[1][0] = Vec2(s.x, 0);		avQuads[1][1] = Vec2(s.x, s.y);	avQuads[1][2] = Vec2(1-s.z, s.y);	avQuads[1][3] = Vec2(1-s.z, 0);		//up
		avQuads[2][0] = Vec2(1-s.z, 0);		avQuads[2][1] = Vec2(1-s.z, 1);	avQuads[2][2] = Vec2(1, 1);			avQuads[2][3] = Vec2(1, 0);			//right
		avQuads[3][0] = Vec2(s.x, 1-s.w);	avQuads[3][1] = Vec2(s.x, 1);	avQuads[3][2] = Vec2(1-s.z, 1);		avQuads[3][3] = Vec2(1-s.z, 1-s.w);	//down
	}

	for(int i = 0; i < 4; ++i)
	{
		DrawQuad(nTexWidth, nTexHeight,
			avQuads[i][0], avQuads[i][1], avQuads[i][2], avQuads[i][3],
			avQuads[i][0], avQuads[i][1], avQuads[i][2], avQuads[i][3]	);
	}

}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::DrawQuad(int nTexWidth, int nTexHeight,
								 const Vec2& vxA, const Vec2& vxB, const Vec2& vxC, const Vec2& vxD,
								 const Vec2& uvA, const Vec2& uvB, const Vec2& uvC, const Vec2& uvD)
{
	// No offsets required in d3d10
	float fOffsetU = 0.0f;
	float fOffsetV = 0.0f;
#if defined (DIRECT3D9) || defined (XENON)
	fOffsetU = 0.5f/(float)nTexWidth;
	fOffsetV = 0.5f/(float)nTexHeight;
#endif

	SVF_P3F_C4B_T2F pScreenQuad[4] =  
	{
		{ Vec3(vxA.x -fOffsetU, vxA.y -fOffsetV, 0), {{0}}, uvA },
		{ Vec3(vxB.x -fOffsetU, vxB.y -fOffsetV, 0), {{0}}, uvB },		
		{ Vec3(vxD.x -fOffsetU, vxD.y -fOffsetV, 0), {{0}}, uvD },
		{ Vec3(vxC.x -fOffsetU, vxC.y -fOffsetV, 0), {{0}}, uvC }
	};
	gRenDev->m_RP.m_PersFlags2 &= ~(RBPF2_COMMIT_PF);  

	CVertexBuffer strip(pScreenQuad, eVF_P3F_C4B_T2F); 




	gRenDev->DrawPrimitivesInternal(&strip, 4, eptTriangleStrip);

}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::DrawFullScreenQuadWPOS(int nTexWidth, int nTexHeight, float z)
{    
	UpdateFrustrumCorners();

  // No offsets required in d3d10
  float fOffsetU = 0.0f;
  float fOffsetV = 0.0f;
#if defined (DIRECT3D9) || defined (XENON)
  fOffsetU = 0.5f/(float)nTexWidth;
  fOffsetV = 0.5f/(float)nTexHeight;  
#endif

  SVF_P3F_T2F_T3F pScreenQuad[] =
  {
    { Vec3(-fOffsetU, -fOffsetV, z), Vec2(0, 0),		m_vLT },   
    { Vec3(-fOffsetU, 1-fOffsetV, z), Vec2(0, 1), 	m_vLB },    
    { Vec3(1-fOffsetU, -fOffsetV, z), Vec2(1, 0), 	m_vRT },   
    { Vec3(1-fOffsetU, 1-fOffsetV, z), Vec2(1, 1),	m_vRB },   
  };

  CVertexBuffer strip(pScreenQuad, eVF_P3F_T2F_T3F);
  



	gRenDev->DrawPrimitivesInternal(&strip, 4, eptTriangleStrip);

}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::DrawScreenQuadWPOS(float x, float y, float sx, float sy, float z, int nTexWidth, int nTexHeight)
{    
//  float fInvW = 1.0f / (float) nTexWidth, fInvH = 1.0f / (float) nTexHeight;
	UpdateFrustrumCorners();

  // No offsets required in d3d10
  float fOffsetU = 0.0f;
  float fOffsetV = 0.0f;
#if defined (DIRECT3D9) || defined (XENON)
  fOffsetU = 0.5f/(float)nTexWidth;
  fOffsetV = 0.5f/(float)nTexHeight;  
#endif

  //x = 0;
  //y = 0;
  //sx = nTexWidth; 
  //sy = nTexHeight;

  float fu1 = ((float) x / (float)nTexWidth);
  float fv1 = ((float) y / (float)nTexHeight);
  float fu2 = ((float) (x+sx) / (float)nTexWidth);
  float fv2 = ((float) (y+sy) / (float)nTexHeight);

  float fx1 = -fOffsetU + fu1;
  float fy1 = -fOffsetV + fv1;
  float fx2 = -fOffsetU + fu2;
  float fy2 = -fOffsetV + fv2;

  SVF_P3F_T2F_T3F pScreenQuad[] =
  {
    { Vec3(fx1, fy1, z), Vec2(fu1, fv1), m_vLT },   
    { Vec3(fx1, fy2, z), Vec2(fu1, fv2), m_vLB },    
    { Vec3(fx2, fy1, z), Vec2(fu2, fv1), m_vRT },   
    { Vec3(fx2, fy2, z), Vec2(fu2, fv2), m_vRB },      
  };

  CVertexBuffer strip(pScreenQuad, eVF_P3F_T2F_T3F);




	gRenDev->DrawPrimitivesInternal(&strip, 4, eptTriangleStrip);

}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::SetTexture(CTexture *pTex, int nStage, int nFilter, int nClamp, bool bSRGBLookup, DWORD dwBorderColor)
{
  int nTexState;

  if (pTex)
  {
    STexState TS;
    TS.SetFilterMode(nFilter);        
    TS.SetClampMode(nClamp, nClamp, nClamp);
    if( nClamp == TADDR_BORDER )
      TS.SetBorderColor(dwBorderColor);
    TS.m_bSRGBLookup = bSRGBLookup;
    nTexState = CTexture::GetTexState(TS);
    pTex->Apply(nStage, nTexState); 
  }
  else
  {
    CTexture::ApplyForID(nStage, 0, -1, -1);
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool SPostEffectsUtils::CreateRenderTarget(const char *szTexName, CTexture *&pTex, int iWidth, int iHeight, bool bUseAlpha, bool bMipMaps, ETEX_Format eTF, int nCustomID, int nFlags, int8 nPriority)
{
  // check if parameters are valid
  if(!iWidth || !iHeight)
  {
    return 0;
  }

  if(pTex)
  {
    pTex->Invalidate(iWidth, iHeight, eTF);

    if(bMipMaps)
    {
      pTex->ResetFlags(FT_NOMIPS);
    }
  }

  uint32 flags = nFlags;
	flags |= FT_DONT_STREAM | FT_USAGE_RENDERTARGET | FT_DONT_RESIZE | (bMipMaps ? FT_FORCE_MIPS : FT_NOMIPS);

  // if texture doesn't exist yet, create it
  if (!CTexture::IsTextureExist(pTex))
  {
    pTex = CTexture::CreateRenderTarget(szTexName, iWidth, iHeight, eTT_2D, flags , eTF, nCustomID, nPriority);      
    if(pTex)
    { 
      // Clear render target surface before using it
      ColorF c = ColorF(0, 0, 0, 1);
      pTex->Fill(c);
    }
  }

  return CTexture::IsTextureExist(pTex) ? 1 : 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::ShBeginPass( CShader *pShader, const CCryNameTSCRC& TechName, uint32 nFlags )
{
  assert( pShader );

  m_pCurrShader = pShader;

  uint32 nPasses;
  m_pCurrShader->FXSetTechnique(TechName);
  m_pCurrShader->FXBegin(&nPasses, nFlags);
  m_pCurrShader->FXBeginPass(0);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::ShEndPass( )
{
  assert( m_pCurrShader );

  m_pCurrShader->FXEndPass();
  m_pCurrShader->FXEnd(); 

  m_pCurrShader = 0;
}  

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::ShSetParamVS( const CCryNameR& pParamName, const Vec4 &pParam )
{
  assert( m_pCurrShader );
  m_pCurrShader->FXSetVSFloat(pParamName, &pParam, 1); 
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::ShSetParamPS( const CCryNameR& pParamName, const Vec4 &pParam )
{
  assert( m_pCurrShader );   
  m_pCurrShader->FXSetPSFloat(pParamName, &pParam, 1); 
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::ClearScreen(float r, float g, float b, float a)
{  
  static CCryNameTSCRC pTechName("ClearScreen");
  ShBeginPass(CShaderMan::m_shPostEffects, pTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

  int iTempX, iTempY, iWidth, iHeight;
  gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

  Vec4 pClrScrParms=Vec4(r, g, b, a); 
  static CCryNameR pParamName("clrScrParams");
  CShaderMan::m_shPostEffects->FXSetPSFloat(pParamName, &pClrScrParms, 1);        

  DrawFullScreenQuad(iWidth, iHeight);

  ShEndPass();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPostEffectsUtils::UpdateFrustrumCorners()
{
  int nFrameID = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_nFrameID;
	if( m_nFrustrumFrameID != nFrameID )
	{
		Vec3 vCoords[8];
		gRenDev->GetRCamera().CalcVerts( vCoords);
		
		m_vRT = vCoords[4] - vCoords[0];
		m_vLT = vCoords[5] - vCoords[1];
		m_vLB = vCoords[6] - vCoords[2];
		m_vRB = vCoords[7] - vCoords[3];

		// Swap order when mirrored culling enabled
		if( ( gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_PersFlags & RBPF_MIRRORCULL) )
		{
			m_vLT = vCoords[4] - vCoords[0];
			m_vRT = vCoords[5] - vCoords[1];
			m_vRB = vCoords[6] - vCoords[2];
			m_vLB = vCoords[7] - vCoords[3];
		}

		m_nFrustrumFrameID = nFrameID;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

Matrix44 &SPostEffectsUtils::GetColorMatrix()
{
	CPostEffectsMgr *pPostMgr = PostEffectMgr();
  int nFrameID = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_nFrameID;
  if( m_nColorMatrixFrameID != nFrameID )
  {
    // Create color transformation matrices
    float fBrightness = pPostMgr->GetByNameF("Global_Brightness");
    float fContrast = pPostMgr->GetByNameF("Global_Contrast");
    float fSaturation = pPostMgr->GetByNameF("Global_Saturation");
    float fColorC = pPostMgr->GetByNameF("Global_ColorC");
    float fColorM = pPostMgr->GetByNameF("Global_ColorM");
    float fColorY = pPostMgr->GetByNameF("Global_ColorY");
    float fColorK = pPostMgr->GetByNameF("Global_ColorK");
    float fColorHue = pPostMgr->GetByNameF("Global_ColorHue");

    float fUserCyan = pPostMgr->GetByNameF("Global_User_ColorC");
    fColorC = fUserCyan;

    float fUserMagenta = pPostMgr->GetByNameF("Global_User_ColorM");
    fColorM = fUserMagenta;

    float fUserYellow = pPostMgr->GetByNameF("Global_User_ColorY");
    fColorY = fUserYellow;

    float fUserLuminance = pPostMgr->GetByNameF("Global_User_ColorK");
    fColorK = fUserLuminance;

    float fUserHue = pPostMgr->GetByNameF("Global_User_ColorHue");
    fColorHue = fUserHue;

    float fUserBrightness = pPostMgr->GetByNameF("Global_User_Brightness");
    fBrightness = fUserBrightness;

    float fUserContrast = pPostMgr->GetByNameF("Global_User_Contrast");
    fContrast = fUserContrast;

    float fUserSaturation = pPostMgr->GetByNameF("Global_User_Saturation"); // translate to 0  
    fSaturation = fUserSaturation;

    // Saturation matrix
    Matrix44 pSaturationMat;      

    {
      float y=0.3086f, u=0.6094f, v=0.0820f, s=clamp_tpl<float>(fSaturation, -1.0f, 100.0f);  

      float a = (1.0f-s)*y + s;
      float b = (1.0f-s)*y;
      float c = (1.0f-s)*y;
      float d = (1.0f-s)*u;
      float e = (1.0f-s)*u + s;
      float f = (1.0f-s)*u;
      float g = (1.0f-s)*v;
      float h = (1.0f-s)*v;
      float i = (1.0f-s)*v + s;

      pSaturationMat.SetIdentity();
      pSaturationMat.SetRow(0, Vec3(a, d, g));  
      pSaturationMat.SetRow(1, Vec3(b, e, h));
      pSaturationMat.SetRow(2, Vec3(c, f, i));                                     
    }

    // Create Brightness matrix
    Matrix44 pBrightMat;
    fBrightness=clamp_tpl<float>(fBrightness, 0.0f, 100.0f);    

    pBrightMat.SetIdentity();
    pBrightMat.SetRow(0, Vec3(fBrightness, 0, 0)); 
    pBrightMat.SetRow(1, Vec3(0, fBrightness, 0));
    pBrightMat.SetRow(2, Vec3(0, 0, fBrightness));            

    // Create Contrast matrix
    Matrix44 pContrastMat;
    {
      float c=clamp_tpl<float>(fContrast, -1.0f, 100.0f);  

      pContrastMat.SetIdentity();
      pContrastMat.SetRow(0, Vec3(c, 0, 0));  
      pContrastMat.SetRow(1, Vec3(0, c, 0));
      pContrastMat.SetRow(2, Vec3(0, 0, c));              
      pContrastMat.SetColumn(3, 0.5f*Vec3(1.0f-c, 1.0f-c, 1.0f-c));  
    }

    // Create CMKY matrix
    Matrix44 pCMKYMat;
    {    
      Vec4 pCMYKParams = Vec4(fColorC + fColorK, fColorM + fColorK, fColorY + fColorK, 1.0f); 

      pCMKYMat.SetIdentity();
      pCMKYMat.SetColumn(3, -Vec3(pCMYKParams.x, pCMYKParams.y, pCMYKParams.z));  
    }

    // Create Hue rotation matrix
    Matrix44 pHueMat;
    {    
      pHueMat.SetIdentity();    

      const Vec3 pHueVec = Vec3(0.57735026f, 0.57735026f, 0.57735026f); // (normalized(1,1,1)
      pHueMat = Matrix34::CreateRotationAA(fColorHue * PI, pHueVec);         
      pHueMat.SetColumn(3, Vec3(0, 0, 0));  
    }

    // Compose final color matrix and set fragment program constants
    m_pColorMat = pSaturationMat * (pBrightMat * pContrastMat * pCMKYMat * pHueMat);      

    m_nColorMatrixFrameID = nFrameID;
  }


  return m_pColorMat;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
